{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 环形编码标记点的生成与识别提取"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%pylab inline\n",
    "from sympy import init_printing,Matrix\n",
    "init_printing(use_latex='mathjax')\n",
    "from scipy import weave\n",
    "import os\n",
    "import cv2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 圆的生成变化和识别\n",
    "\n",
    "圆会变成椭圆, 因此实际上是识别椭圆"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "h,w = 480,640\n",
    "black, white = 0, 255\n",
    "img = np.zeros((h,w),dtype=np.uint8)\n",
    "cv2.ellipse( img, (w/2, h/2), (100,70), 60, 0, 360, white, -1 )\n",
    "imshow(img,cmap='gray')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "contours, hierarchy = cv2.findContours(img,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "cimg = np.zeros((h,w,3),dtype=np.uint8)\n",
    "cv2.drawContours(cimg,contours,0,(255,255,0),2)\n",
    "imshow(cimg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def get_ellipse_sq(contour):\n",
    "    # 最小二乘法估计椭圆轮廓参数\n",
    "    _A = None\n",
    "    _B = None\n",
    "    for point in contour:\n",
    "        x,y = point[0]\n",
    "        a = mat([x*y,y**2,x,y,1])\n",
    "        b = mat([-x**2])\n",
    "        if _A is None:\n",
    "            _A = a\n",
    "            _B = b\n",
    "        else:\n",
    "            _A = np.vstack((_A,a))\n",
    "            _B = np.vstack((_B,b))\n",
    "    X = np.linalg.pinv(_A)*_B\n",
    "    A,B,C,D,E = np.array(X).flatten()\n",
    "    xc = (A*D - 2*B*C)/(4*B - A**2)\n",
    "    yc = (A*C - 2*D)/(4*B - A**2)\n",
    "    a = np.sqrt(2*(B*C**2 + D**2 - A*C*D - 4*B*E + A**2*E)/((4*B-A**2)*(B+1-np.sqrt(A**2-(B-1)**2))))\n",
    "    b = np.sqrt(2*(B*C**2 + D**2 - A*C*D - 4*B*E + A**2*E)/((4*B-A**2)*(B+1+np.sqrt(A**2-(B-1)**2))))\n",
    "    th = np.arctan((B*b**2 - a**2)/(b**2-B*a**2))\n",
    "    return xc,yc,a,b,th"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "contour = contours[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "xc,yc,a,b,th = get_ellipse_sq(contour)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "cv2.ellipse(cimg,(int(xc),int(yc)),(int(a),int(b)),np.rad2deg(th),0,360,(255,0,0),2)\n",
    "imshow(cimg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 直接用 opencv API fitEllipse\n",
    "\n",
    "``` C++\n",
    "    cv::RotatedRect box = cv::fitEllipse(pointsf);// 椭圆形匹配      \n",
    "    cv::ellipse(src_img, box, cv::Scalar(0,0,255), 2, CV_AA);// 绘制出椭圆 \n",
    "```\n",
    "box = ((xc,yc),(2b,2a),180-th) 注意th的方向 ??\n",
    "``` C++\n",
    "RotatedRect ()\n",
    " \tvarious constructors More...\n",
    " \n",
    " \tRotatedRect (const Point2f &center, const Size2f &size, float angle)\n",
    " \n",
    " \tRotatedRect (const Point2f &point1, const Point2f &point2, const Point2f &point3)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "box = cv2.fitEllipse(contour)\n",
    "cv2.ellipse(cimg,box,(0,0,255),2)\n",
    "imshow(cimg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 编码和解码生成\n",
    "\n",
    "### 生成，编码\n",
    "\n",
    "将圆 N 等分，像 Nao Mark 一样, 用 0,1 来生成编码，\n",
    "\n",
    "并通过轮询获得最小值作为实际的编码值，即将最多的连续0放在最前面。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "n = 0b101011"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "1<<1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "decode(2,8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def decode(n,num):\n",
    "    code_list = []\n",
    "    max_bit = 2**num\n",
    "    for x in range(num):\n",
    "        n = n<<1 # 直接左移，最后一位默认置为0\n",
    "        if n & max_bit: # 超出的一位为 1\n",
    "            n = n - max_bit # 超出的一位，置为0\n",
    "            n = n + 1 # 将最后一位置为1\n",
    "        code_list.append(n)\n",
    "    return np.min(code_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def get_all_code(num=8):\n",
    "    code_list = []\n",
    "    for n in range(2**num-1):\n",
    "        code = decode(n,num)\n",
    "        if code not in code_list:\n",
    "            code_list.append(code)\n",
    "    return code_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def code2list(n,num):\n",
    "    return [(n&(2**i))/(2**i) for i in range(num)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "all_code = get_all_code(8)\n",
    "len(all_code)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def _gen_n(im,r,min_r,min_th,max_th):\n",
    "    I = im\n",
    "    expr = \"\"\"\n",
    "    double pi = 3.14159265358979323846;\n",
    "    double center_y = NI[0] / 2.0;\n",
    "    double center_x = NI[1] / 2.0;\n",
    "    for (int y = 1; y < NI[0]-1; y++) {\n",
    "        double dy = y - center_y;\n",
    "        for (int x = 1; x < NI[1]-1; x++) {\n",
    "            double dx = x = center_x;\n",
    "            double d = sqrt(dx*dx + dy*dy);\n",
    "            if(d > min_r && d < r){\n",
    "                double th = atan2(dy,dx);\n",
    "                if(th < 0){\n",
    "                    th = th + 2*pi;\n",
    "                }\n",
    "                if(th > min_th && th < max_th){\n",
    "                    I2(y, x) = 255;\n",
    "                }\n",
    "            }\n",
    "        }\n",
    "    }\n",
    "    \"\"\"\n",
    "    weave.inline(expr, [\"I\", \"r\",\"min_th\",\"max_th\",\"min_r\"],\n",
    "                 include_dirs=[\"C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\include\"],\n",
    "                 headers=[\"<math.h>\"])\n",
    "    return I"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def gen_n(img,r,n,num,min_r=0):\n",
    "    '''\n",
    "    绘制一个编码\n",
    "    img: 绘制图像\n",
    "    r: 圆半径\n",
    "    n: 绘制角度，绘制角度范围为 2pi*n/num 到 (n+1)\n",
    "    num: 总的编码长度\n",
    "    '''\n",
    "    h,w = img.shape\n",
    "    center = mat([w/2,h/2],dtype=np.int).T\n",
    "    min_th = 2*pi*n/num\n",
    "    max_th = min_th + 2*pi/num\n",
    "    for x in range(w):\n",
    "        for y in range(h):\n",
    "            p = mat([x,y],dtype=np.int).T\n",
    "            v = p-center\n",
    "            #d = np.sqrt(v[0,0]**2+v[1,0]**2)\n",
    "            d = np.linalg.norm(v)\n",
    "            if min_r < d < r:\n",
    "                th = np.arctan2(v[1,0],v[0,0])\n",
    "                th = th + 2*pi if th < 0 else th\n",
    "                if min_th <= th <= max_th:\n",
    "                    img[y,x] = 255"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def gen_mark(code,r,num=12,thikness=10):\n",
    "    '''\n",
    "    code: 编码，长于num相同，0表示编码1表示不编码\n",
    "    r: 绘制半径\n",
    "    num: 等分数\n",
    "    thikness: 外圆宽度\n",
    "    '''\n",
    "    w = 2*int(r+thikness)\n",
    "    h = w\n",
    "    img = np.zeros((h,w),dtype=np.uint8)\n",
    "    cv2.circle(img,(int(w/2),int(h/2)),r,(255,255,255),thikness) # 绘制外圆\n",
    "    assert len(code) == num\n",
    "    for i,v in enumerate(code):\n",
    "        if v == 1:\n",
    "            gen_n(img,r,i,num,min_r=0.2*r)\n",
    "    return img"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "code = [1,0,1,1,0,1,1,0]\n",
    "r = 200\n",
    "img = gen_mark(code,r,num=8,thikness=int(r*0.1))\n",
    "imshow(img,cmap='gray')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "tmp = np.zeros((480,640),np.uint8)\n",
    "h,w = img.shape\n",
    "tmp[10:10+h,10:10+w] = img\n",
    "imshow(tmp,cmap='gray')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "imshow(255-img,cmap='gray')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 解码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def alpc(img,M,a,b,x0,y0,th):\n",
    "    out = np.zeros_like(img)\n",
    "    h,w = img.shape\n",
    "    ct = cos(th)\n",
    "    st = sin(th)\n",
    "    s = a / b\n",
    "    for x in range(w):\n",
    "        dx = x - x0\n",
    "        for y in range(h):\n",
    "            dy = y - y0\n",
    "            tmp = np.math.sqrt((dx*ct+dy*st)**2 + s**2*(dy*ct-dx*st)**2)\n",
    "            _x = M*log(tmp)\n",
    "            _y = arctan2(s*dy*ct - dx*st, dx*ct+dy*st) + pi\n",
    "            if _x < 0: continue\n",
    "            if _x > w: continue\n",
    "            _y = _y/(2*pi) * h - 1\n",
    "            out[_y,_x] = img[y,x]\n",
    "    return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "imshow(out,cmap=\"gray\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "h,w = img.shape\n",
    "x = int(w/2)\n",
    "y = int(h/2)\n",
    "out = np.zeros_like(img)\n",
    "cvimg = cv2.cv.fromarray(img)\n",
    "cvout = cv2.cv.fromarray(out)\n",
    "cv2.cv.LogPolar(cvimg,cvout,(x,y),20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "imshow(cvout,cmap=\"gray\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 保存所有标记到图像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def save_codes(num=12,r=200):\n",
    "    all_code = get_all_code(num)\n",
    "    for code in all_code:\n",
    "        print \"gen code:\", code\n",
    "        code_list = code2list(code,num)\n",
    "        img = gen_mark(code_list,r,num,thikness=int(r*0.1))\n",
    "        cv2.imwrite(str(code)+\".jpg\",255-img)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#save_codes()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
